﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.CompilerServices;
using System.Text;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Windows.Forms;
using SolidWorks.Interop.sldworks;

// Credit: http://www.jasonnewell.net/

namespace SwAddinHelpers.ComHelpers
{
    /*public class AxControl : AxHost
    {
        public AxControl(string strCLSID)
            : base(strCLSID)
        {

        }
    }*/
    
    /*
    // moved out of proc due to EIT
    // http://stackoverflow.com/questions/1596730/visual-studio-2010-embed-interop-types
    public static class TypeConverterExtension
    {
        public static Type GetManagedTypeForSWObject(this object comObject)
        {
            Assembly asm = typeof(SolidWorks.Interop.sldworks.SldWorks).Assembly;

            IntPtr ukwn = Marshal.GetIUnknownForObject(comObject);

            foreach (Type t in asm.GetTypes())
            {
                Guid gid = t.GUID;
                if (t.IsInterface & gid != Guid.Empty)
                {
                    IntPtr oPtr = IntPtr.Zero;
                    Marshal.QueryInterface(ukwn, ref gid, out oPtr);

                    if (oPtr != IntPtr.Zero && t.FullName.StartsWith("SolidWorks"))
                    {
                        return t;
                    }
                }
            }
            return null;

        }
    }
    */

    [Guid("00020400-0000-0000-c000-000000000046"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IDispatch
    {
        int GetTypeInfoCount();
        System.Runtime.InteropServices.ComTypes.ITypeInfo
            GetTypeInfo([MarshalAs(UnmanagedType.U4)] int iTInfo,
            [MarshalAs(UnmanagedType.U4)] int lcid);
        [PreserveSig]
        int GetIDsOfNames(ref Guid riid,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
            string[] rgsNames, int cNames, int lcid,
            [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
        [PreserveSig]
        int Invoke(int dispIdMember, ref Guid riid,
            [MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
            int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
            pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
            pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
            pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
    }

    [ComVisible(true)]
    [Guid("00020400-0000-0000-c000-000000000046"),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IDispatch2
    {
        [PreserveSig]
        int Invoke(int dispIdMember, ref Guid riid,
            [MarshalAs(UnmanagedType.U4)] int lcid, [MarshalAs(UnmanagedType.U4)]
            int dwFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS
            pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[]
            pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO
            pExcepInfo, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] pArgErr);
    }

    /// <summary>
    /// Required Interface for implementing dynamic callback functionality
    /// </summary>
    [ComVisible(true)]
    [Guid("00020400-0000-0000-c000-000000000046"),
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IDispatch3
    {
    }

    public class ComObject : IDisposable
    {
        private object _object;
        private IDispatch _dispatch;
        private IntPtr _pTypeAttr = IntPtr.Zero;
        private System.Runtime.InteropServices.ComTypes.ITypeInfo _typeInfo;
        private string _typeName, _typeDescription, _typeHelpFile;
        private int _typeHelpContext;
        //private SolidEdgeSpy.InteropServices.ComTypeLibrary _comTypeLibrary;

        public ComObject(object comObject)
        {
            System.Runtime.InteropServices.ComTypes.ITypeLib ppTLB = null;
            int pIndex = 0;

            _dispatch = comObject as IDispatch;

            if (_dispatch != null)
            {
                _object = comObject;
                //int c = _dispatch.GetTypeInfoCount();
                _typeInfo = _dispatch.GetTypeInfo(0, 0);
                _typeInfo.GetTypeAttr(out _pTypeAttr);
                _typeInfo.GetDocumentation(-1, out _typeName, out _typeDescription, out _typeHelpContext, out _typeHelpFile);
                _typeInfo.GetContainingTypeLib(out ppTLB, out pIndex);
                //_comTypeLibrary = new ComTypeLibrary(ppTLB);
            }
            else
            {
                throw new InvalidComObjectException();
            }
        }

        ~ComObject()
        {
            Dispose();
        }

        public void Dispose()
        {
            try
            {
                if (_typeInfo != null)
                {
                    _typeInfo.ReleaseTypeAttr(_pTypeAttr);
                }

                if (_object != null) { Marshal.ReleaseComObject(_object); _object = null; }
                if (_dispatch != null) { Marshal.ReleaseComObject(_dispatch); _dispatch = null; }
            }
            catch
            {
            }
        }

        public string[] GetPropertyNames()
        {
            System.Collections.ArrayList list = new System.Collections.ArrayList();

            try
            {
                for (int i = 0; i < this.TypeAttr.cFuncs; i++)
                {
                    IntPtr pFuncDesc = IntPtr.Zero;
                    System.Runtime.InteropServices.ComTypes.FUNCDESC funcDesc;
                    string strName, strDocString, strHelpFile;
                    int dwHelpContext;

                    _typeInfo.GetFuncDesc(i, out pFuncDesc);
                    funcDesc = (System.Runtime.InteropServices.ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc,
                        typeof(System.Runtime.InteropServices.ComTypes.FUNCDESC));

                    switch (funcDesc.invkind)
                    {
                        case
                            System.Runtime.InteropServices.ComTypes.INVOKEKIND.INVOKE_PROPERTYGET:
                                _typeInfo.GetDocumentation(funcDesc.memid,
                                out strName, out strDocString, out dwHelpContext, out strHelpFile);
                                list.Add(strName);
                                break;
                    }
                }
            }
            catch (System.Exception ex)
            {
                throw ex;
            }

            return (string[])list.ToArray(typeof(string));
        }

        public string TypeName { get { return this._typeName; } }
        //public string TypeFullName { get { return this.ComTypeLibrary.Name + "." + this._typeName; } }
        public string TypeDescription { get { return this._typeDescription; } }
        public int TypeHelpContext { get { return this._typeHelpContext; } }
        public string TypeHelpFile { get { return this._typeHelpFile; } }
        public object WrappedComObject { get { return _dispatch; } }
        //public ComTypeLibrary ComTypeLibrary { get { return _comTypeLibrary; } }
        public System.Runtime.InteropServices.ComTypes.TYPEATTR
        TypeAttr { get { return (System.Runtime.InteropServices.ComTypes.TYPEATTR)Marshal.PtrToStructure(_pTypeAttr,
            typeof(System.Runtime.InteropServices.ComTypes.TYPEATTR)); } }
        public string TypeVersion
        {
            get
            {
                string version = String.Empty;
                try
                {
                    version = TypeAttr.wMajorVerNum.ToString() + "." +  TypeAttr.wMinorVerNum.ToString();
                }
                catch
                {
                }
                return version;
            }
        }
    }

    public class ComTypeLibrary
    {
        private System.Runtime.InteropServices.ComTypes.ITypeLib _typeLib;
        IntPtr _pTypeLibAttr = IntPtr.Zero;
        string _Name, _Description, _HelpFile;
        int _HelpContext;

        public ComTypeLibrary(System.Runtime.InteropServices.ComTypes.ITypeLib typeLib)
        {
            _typeLib = typeLib;
            _typeLib.GetLibAttr(out _pTypeLibAttr);
            _typeLib.GetDocumentation(-1, out _Name, out _Description,
                out _HelpContext, out _HelpFile);
        }

        public string Name { get { return this._Name; } }
        public string Description { get { return this._Description; } }
        public int HelpContext { get { return this._HelpContext; } }
        public string HelpFile { get { return this._HelpFile; } }
        public System.Runtime.InteropServices.ComTypes.TYPELIBATTR
            TypeLibAttr { get { return
                (System.Runtime.InteropServices.ComTypes.TYPELIBATTR)Marshal.PtrToStructure(_pTypeLibAttr,
                typeof(System.Runtime.InteropServices.ComTypes.TYPELIBATTR)); } }
        public string TypeLibVersion
        {
            get
            {
                string version = String.Empty;
                try
                {
                    version = TypeLibAttr.wMajorVerNum.ToString() + "." + TypeLibAttr.wMinorVerNum.ToString();
                }
                catch
                {
                }
                return version;
            }
        }

        public Version Version
        {
            get
            {
                return new Version(TypeLibAttr.wMajorVerNum, TypeLibAttr.wMinorVerNum, 0, 0);
            }
        }

        public override int GetHashCode()
        {
            return this.TypeLibAttr.guid.GetHashCode() +
                this.TypeLibAttr.wMajorVerNum.GetHashCode() +
                this.TypeLibAttr.wMinorVerNum.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            ComTypeLibrary comTypeLibrary = obj as ComTypeLibrary;
            if (comTypeLibrary != null)
            {
                if (this.TypeLibAttr.guid.Equals(comTypeLibrary.TypeLibAttr.guid))
                {
                    if (this.TypeLibAttr.wMajorVerNum.Equals(comTypeLibrary.TypeLibAttr.wMajorVerNum))
                    {
                        if (this.TypeLibAttr.wMinorVerNum.Equals(comTypeLibrary.TypeLibAttr.wMinorVerNum))
                        {
                            return true;
                        }
                    }
                }
                return base.Equals(obj);
            }
            else
            {
                return base.Equals(obj);
            }
        }

        public override string ToString()
        {
            return this.Name + " - " + this.Description;
        }
    }

}
